﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;

namespace YEngineLib.Animation
{
    public struct YAnimationFrame
    {
        public Rectangle sourceRect;
        public Vector2 anchorPoint;
        public float duration;
    }

    public class YAnimationData
    {
        public string name;
        public Texture2D animImage;
        //public Vector2 origin = Vector2.Zero;

        public List<YAnimationFrame> frames = new List<YAnimationFrame>();

        public bool isLooping;

        public bool isOscillating;

        public void AddFrame(Rectangle sourceRect, Vector2 anchorPoint, float duration)
        {
            YAnimationFrame animFrame = new YAnimationFrame();
            animFrame.sourceRect = sourceRect;
            animFrame.anchorPoint = anchorPoint;
            animFrame.duration = duration;

            AddFrame(animFrame);
        }

        public void AddFrame(YAnimationFrame animFrame)
        {
            frames.Add(animFrame);
        }

        public YAnimationData(Texture2D tex, bool isLooping, bool isOscillating)
        {
            animImage = tex;

            this.isLooping = isLooping;
            this.isOscillating = isOscillating;
        }
    }

    public class YAnimationPlayer
    {
        public YAnimationData animData;

        int curFrame;

        float timeWaited;

        float speed;

        bool isPlaying;
        bool isLooping;

        bool isAnimForward;

        public void SetAnimationData(YAnimationData animData)
        {
            this.animData = animData;

            isLooping = (animData != null) ? animData.isLooping : true;

            isAnimForward = true;

            speed = 1.0f;

            Stop();
            Reset();
        }

        public int CurFrame
        {
            get { return curFrame; }
            set { curFrame = value; }
        }

        public float PlaySpeed
        {
            get
            {
                return speed;
            }
            set
            {
                speed = value;
            }
        }

        public string AnimName
        {
            get { return animData.name; }
        }

        public bool IsPlaying
        {
            get
            {
                return isPlaying;
            }
            set
            {
                isPlaying = value;
            }
        }

        public YAnimationFrame GetCurFrame()
        {
            Debug.Assert(animData != null);

            return animData.frames[curFrame];
        }

        private Rectangle GetFrameRect(int id)
        {
            Debug.Assert(animData != null);
            Debug.Assert(id >= 0 && id < animData.frames.Count);

            Rectangle r = animData.frames[id].sourceRect;
            return r;
        }

        public void Reset()
        {
            isAnimForward = true;
            timeWaited = 0.0f;
            curFrame = 0;
        }

        public YAnimationPlayer()
        {
            SetAnimationData(null);

            speed = 1.0f;

            Stop();
            Reset();
        }

        public void Play(YAnimationData animData)
        {
            SetAnimationData(animData);

            Play();
        }

        public void Play()
        {
            Reset();
            Resume();
        }

        public void Stop()
        {
            isPlaying = false;
        }

        public void Resume()
        {
            isPlaying = true;
        }

        public void PlayBackwards()
        {
            ResetBackwards();
            Resume();
        }

        public void ResetBackwards()
        {
            isAnimForward = false;
            timeWaited = 0.0f;
            curFrame = animData.frames.Count-1;
        }

        public void Update(float dt)
        {
            if (animData == null)
                return;

            if (!isPlaying)
                return;

            timeWaited += dt * speed;

            if (timeWaited > GetCurFrame().duration)
            {
                timeWaited = 0.0f;

                if (isAnimForward)
                {
                    curFrame++;

                    if (curFrame >= animData.frames.Count)
                    {
                        if (animData.isOscillating)
                        {
                            isAnimForward = false;
                            timeWaited = 0.0f;
                            curFrame = animData.frames.Count - 2;

                            if (curFrame < 0)
                                curFrame = 0;

                            return;
                        }

                        if (isLooping)
                        {
                            Reset();
                        }
                        else
                        {
                            Stop();
                            curFrame = animData.frames.Count - 1;
                        }
                    }
                }
                //else if (animData.isOscillating && !isAnimForward)
                else if (!isAnimForward)
                {
                    curFrame--;

                    if (curFrame < 0)
                    {
                        if (isLooping)
                        {
                            if (animData.isOscillating)
                            {
                                Reset();
                                curFrame = 1; // go forward one frame
                            }
                            else
                            {
                                ResetBackwards();
                            }
                        }
                        else
                        {
                            Stop();
                            curFrame = 0;
                        }
                    }
                }
            }
        }

        public void Draw(SpriteBatch sprite, Vector2 pos)
        {
            Draw(sprite, pos, false, Vector2.One, Color.White, 0.0f);
        }

        public void Draw(SpriteBatch sprite, Vector2 pos, bool isFlipped, Vector2 scale, Color color, float rotation)
        {
            Debug.Assert(animData != null);

            Rectangle src = GetCurFrame().sourceRect;
            Vector2 anchor = GetCurFrame().anchorPoint;

            SpriteEffects fx = SpriteEffects.None;

            if (isFlipped)
            {
                fx = SpriteEffects.FlipHorizontally;
                anchor.X = src.Width - anchor.X;
            }

            sprite.Draw(animData.animImage, pos, src, color, rotation, anchor, scale, fx, 0.0f);
        }
    }
}
